package com.chao.auth.config;

import com.chao.auth.service.CustomAuthorizationCodeServices;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;

import java.security.KeyPair;
import java.util.ArrayList;
import java.util.List;

/**
 * 
 * @Description:   
 * @className: Oauth2ServerConfig 
 * @author fu wenchao
 * @date 2022/1/24 21:51 
 * @company:西安博达软件股份有限公司
 * @copyright: Copyright (c) 2022
 * @version V1.0 
 */
@Configuration
@EnableAuthorizationServer
public class OauthServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private JwtTokenEnhancer jwtTokenEnhancer;
    @Autowired
    private JwtAccessTokenConverter jwtTokenConverter;
    @Autowired
    private ClientDetailsService clientDetailsService;
    @Autowired
    private UserDetailsService customUserService;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private TokenStore tokenStore;

    /**
     * 令牌访问端口安全策略
     *     //设置 /oauth/check_token 端点，通过认证后可访问。
     *     //这里的认证，指的是使用 client-id + client-secret 进行的客户端认证，不要和用户认证混淆。
     *     //其中，/oauth/check_token 端点对应 CheckTokenEndpoint 类，用于校验访问令牌的有效性。
     *     //在客户端访问资源服务器时，会在请求中带上访问令牌。
     *     //在资源服务器收到客户端的请求时，会使用请求中的访问令牌，找授权服务器确认该访问令牌的有效性。
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                //jwt非对称加密暴露端点    开启/oauth/token_key验证端口权限访问
                .tokenKeyAccess("permitAll()")
                //开启/oauth/check_token验证端口认证权限访问
                .checkTokenAccess("isAuthenticated()")
                .allowFormAuthenticationForClients();
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory().withClient("oauth2-demo")
                .secret(passwordEncoder.encode("861023792"))
                .authorizedGrantTypes("password","refresh_token","authorization_code","client_credentials","implicit")
                .scopes("read","write")
                .redirectUris("https://www.baidu.com")
                .authorities("ADMIN")
                //自动授权
                .autoApprove(false)
                .accessTokenValiditySeconds(60 * 30)
                .refreshTokenValiditySeconds(60 * 60 * 24 * 7);
    }

    /**
     * 设置授权码模式的授权码如何获取
     */
    @Bean
    public AuthorizationCodeServices codeServices(){
//        return new InMemoryAuthorizationCodeServices();
        return new CustomAuthorizationCodeServices();
    }

    /**
     * 配置令牌的访问端点和令牌服务
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> delegates = new ArrayList<>();
        delegates.add(jwtTokenEnhancer);
        //配置JWT的内容增强器
        enhancerChain.setTokenEnhancers(delegates);
        endpoints.authenticationManager(authenticationManager)
                .userDetailsService(customUserService)
                //授权码服务
                .authorizationCodeServices(codeServices())
                //令牌管理服务
                .tokenServices(tokenServices())
                .tokenEnhancer(enhancerChain)
                .allowedTokenEndpointRequestMethods(HttpMethod.POST)
                .accessTokenConverter(jwtTokenConverter)
                .reuseRefreshTokens(false);
    }

    /**
      * @description 从ClassPath下获取密钥
      *  keytool -genkey -alias oauth -keypass 861023 -keyalg RSA -sigalg sha256withrsa -keysize 2048 -validity 365 -keystore oauth.jks -storepass 861023
      * @author Rookie
      * @date 2022/1/25 22:15
      * @param
      * @return java.security.KeyPair
      */
    @Bean
    public KeyPair keyPair(){
        KeyStoreKeyFactory keyFactory = new KeyStoreKeyFactory(new ClassPathResource("oauth.jks"), "861023".toCharArray());
        return keyFactory.getKeyPair("oauth","861023".toCharArray());
    }

    /**
     * 令牌管理服务的配置
     */
    @Bean
    public AuthorizationServerTokenServices tokenServices(){
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        //客户端端配置策略
        defaultTokenServices.setClientDetailsService(clientDetailsService);
        defaultTokenServices.setAuthenticationManager(authenticationManager);
        //支持令牌刷新
        defaultTokenServices.setSupportRefreshToken(true);
        //令牌存储
        defaultTokenServices.setTokenStore(tokenStore);
        //设置令牌增强，使用JwtAccessTokenConverter进行转换
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> tokenEnhancers = new ArrayList<>();
        tokenEnhancers.add(jwtTokenEnhancer);
        tokenEnhancers.add(jwtTokenConverter);
        enhancerChain.setTokenEnhancers(tokenEnhancers);
        defaultTokenServices.setTokenEnhancer(enhancerChain);
        return defaultTokenServices;
    }
}
